import * as is  from './is';
export const INTERNAL = Symbol('INTERNAL');
/**
 * 
 * @param {*} baseState 原状态
 * @param {*} producer 处理器或者说生产者
 */
export function produce(baseState, producer){
    const proxy = toProxy(baseState);
    producer(proxy);
    const internal = proxy[INTERNAL]
    return internal.mutated ? internal.draftState : internal.baseState
}

export function toProxy(baseState,valueChange){
    let keyToProxy = {}; //key映射的代理对象
    let internal = {
        baseState,
        draftState: createDraftState(baseState), //浅拷贝,指向老的状态
        keyToProxy,
        mutated: false //此对象是否发生了变更
    }
    return new Proxy(baseState,{
        get(target,key){
            if(key === INTERNAL){ //如果访问代理对象的INTERNAL属性，就返回这个internal对象
                return internal
            }
            let value = target[key] //baseState.list = ['1']
            // 当访问某个属性的时候，就要对这个属性进行代理
            if(is.isObject(value)||is.isArray(value)){
                if(key in keyToProxy){
                    return keyToProxy[key]
                }else{
                    keyToProxy[key] = toProxy(value,()=>{
                        internal.mutated = true; //任何一个子节点变了，自己也会变
                        const proxyOfChild = keyToProxy[key];
                        const { draftState } = proxyOfChild[INTERNAL];
                        internal.draftState[key] = draftState;
                        valueChange && valueChange();
                    })
                }
                return keyToProxy[key]
            }
            return internal.mutated ? internal.draftState[key] : internal.baseState[key]
            
        },
        set(target,key,value){
            internal.mutated = true; //不管给哪个字段修改值，都需要把internal.mutated设置成true
            let {draftState}= internal;
            // for (const key in target) {
            //     draftState[key] = key in draftState ? draftState[key] : target[key];
            // }
            draftState[key] = value;
            valueChange && valueChange(); //调用父级的拷贝，也生成新节点
            return true;
        }
    })
}   

function createDraftState(baseState){
    if(is.isArray(baseState)){
        return [...baseState];
    }else if(is.isObject(baseState)){
        return Object.assign({}, baseState);
    }else{
        return baseState;
    }
}  